# -*- coding: utf-8 -*-
from __future__ import unicode_literals
import frappe
import requests
import json
from frappe import _
from frappe.utils import get_url, cint, cstr
import urllib.parse
import os
import datetime

def log_to_file(message, log_type="Info"):
    """
    将日志写入文本文件
    """
    try:
        # 创建日志目录
        log_dir = os.path.join(frappe.utils.get_site_path(), 'logs', 'maxkey_sso')
        if not os.path.exists(log_dir):
            os.makedirs(log_dir)
        
        # 创建日志文件名（按日期）
        today = datetime.datetime.now().strftime('%Y-%m-%d')
        log_file = os.path.join(log_dir, f'maxkey_sso_{today}.txt')
        
        # 获取当前时间
        timestamp = datetime.datetime.now().strftime('%Y-%m-%d %H:%M:%S')
        
        # 写入日志
        with open(log_file, 'a') as f:
            f.write(f"[{timestamp}] [{log_type}] {message}\n")
            
    except Exception as e:
        # 如果写入日志失败，不应该抛出异常，而是静默失败
        pass

@frappe.whitelist(allow_guest=True)
def login():
    """
    启动 MaxKey OAuth 登录流程
    将用户重定向到 MaxKey 授权页面
    """
    # 获取配置
    config = get_config()
    if not config or not config.enabled:
        frappe.throw(_("MaxKey SSO 未启用，请联系系统管理员"))
    
    # 构建授权 URL
    params = {
        "client_id": config.client_id,
        "response_type": "code",
        "redirect_uri": get_redirect_uri()
    }
    
    # 添加额外参数
    if hasattr(frappe.local, "request"):
        # 保存当前 URL，以便登录后重定向回来
        redirect_to = frappe.local.request.args.get("redirect-to")
        if redirect_to:
            frappe.cache().set_value(
                f"maxkey_sso_redirect:{frappe.generate_hash()[:10]}",
                redirect_to,
                expires_in_sec=60
            )
    
    # 构建完整的授权 URL
    auth_url = config.get_auth_url() + "?" + urllib.parse.urlencode(params)
    
    # 重定向用户到 MaxKey 登录页面
    frappe.local.response["type"] = "redirect"
    frappe.local.response["location"] = auth_url
    
    # 记录日志
    log_to_file(f"正在重定向到 MaxKey 授权 URL: {auth_url}", "MaxKey SSO Login")

# 以下是修改后的代码，主要修改了获取访问令牌的部分

@frappe.whitelist(allow_guest=True)
def callback():
    """
    处理 MaxKey OAuth 回调
    用授权码交换访问令牌，获取用户信息并登录
    """
    # 记录日志
    log_to_file(f"收到 MaxKey 回调: {frappe.request.args}", "MaxKey SSO Callback")
    
    # 检查是否有错误
    if "error" in frappe.request.args:
        error = frappe.request.args.get("error")
        error_description = frappe.request.args.get("error_description", "")
        log_to_file(f"MaxKey 授权错误: {error} - {error_description}", "MaxKey SSO Error")
        
        frappe.local.response["type"] = "redirect"
        frappe.local.response["location"] = "/login?error=maxkey_error"
        return
    
    # 获取授权码
    code = frappe.request.args.get("code")
    if not code:
        log_to_file("未收到授权码", "MaxKey SSO Error")
        frappe.throw(_("未收到授权码"))
    
    # 获取配置
    config = get_config()
    if not config or not config.enabled:
        frappe.throw(_("MaxKey SSO 未启用，请联系系统管理员"))
    
    # 用授权码交换访问令牌
    #token_response = None
    try:
        token_url = config.get_token_url()
        
        token_data = {
            "client_id": config.client_id,
            "client_secret": config.client_secret,
            "grant_type": "authorization_code",
            "code": code,
            "redirect_uri": get_redirect_uri()
        }
        '''
        token_data = {
            "client_id": "1117043445307801600",
            "client_secret": "5w5JMDYwNTIwMjUwMDEyNTY2NTcAgn",
            "grant_type": "authorization_code",
            "code": code,
            "redirect_uri": "http://117.161.7.82:18001/api/method/erpnext_maxkey_sso.erpnext_maxkey_sso.oauth.callback"
        }
        '''
        log_to_file(f"client_secret:{config.client_secret}")
        # 记录请求信息（隐藏敏感信息）
        safe_token_data = token_data.copy()
        if "client_secret" in safe_token_data:
            safe_token_data["client_secret"] = "5w5JMDYwNTIwMjUwMDEyNTY2NTcAgn"
        log_to_file(f"正在请求访问令牌: URL={token_url}, Data={json.dumps(safe_token_data)}", "MaxKey SSO Token Request")
        headers = {
            "Content-Type": "application/x-www-form-urlencoded",
            "Accept": "application/json"
            }
        token_response = requests.post(url=token_url,data=token_data,headers=headers)
        log_to_file(f"Token 响应:{token_response.text}")
        # 记录响应状态和内容
        response_text = token_response.text or "(空响应)"
        log_to_file(f"Token 响应: Status={token_response.status_code}, Headers={dict(token_response.headers)}, Body={response_text}", "MaxKey SSO Token Response")
        
        # 检查响应状态码
        if token_response.status_code != 200:
            frappe.throw(_("获取访问令牌失败: {}").format(response_text))
        
        # 检查响应内容是否为空
        if not token_response.text or token_response.text.strip() == "":
            log_to_file("收到空响应", "MaxKey SSO Error")
            frappe.throw(_("MaxKey 服务器返回了空响应"))
            
        # 直接解析 JSON，不尝试 URL 编码解析
        try:
            token_data = token_response.json()
        except json.JSONDecodeError as e:
            log_to_file(f"JSON 解析错误: {str(e)}, 原始响应: {response_text}", "MaxKey SSO Error")
            frappe.throw(_("无法解析 MaxKey 服务器响应: {}").format(str(e)))
            
        # 获取访问令牌
        access_token = token_data.get("access_token")
        if not access_token:
            log_to_file(f"响应中没有访问令牌: {json.dumps(token_data)}", "MaxKey SSO Error")
            frappe.throw(_("响应中没有访问令牌"))
            
    except requests.exceptions.RequestException as e:
        log_to_file(f"HTTP 请求异常: {str(e)}", "MaxKey SSO Error")
        frappe.throw(_("连接 MaxKey 服务器时出错: {}").format(str(e)))
    except Exception as e:
        log_to_file(f"获取令牌异常: {str(e)}", "MaxKey SSO Error")
        if token_response:
            log_to_file(f"令牌响应: {token_response.text or '(空响应)'}", "MaxKey SSO Response")
        frappe.throw(_("获取访问令牌时出错: {}").format(str(e)))
    
    # 获取用户信息 - 同样使用 Bearer 令牌方式
    try:
        userinfo_url = config.get_userinfo_url()
        
        log_to_file(f"正在请求用户信息: URL={userinfo_url}, Token={access_token}", "MaxKey SSO UserInfo Request")
        
        # 修改：使用 Bearer 令牌方式
        headers = {
            "Authorization": f"Bearer {access_token}",
            "Accept": "application/json"
        }
        
        user_response = requests.get(
            userinfo_url,
            headers=headers,
            timeout=30
        )
        
        # 记录响应
        response_text = user_response.text or "(空响应)"
        log_to_file(f"用户信息响应: Status={user_response.status_code}, Headers={dict(user_response.headers)}, Body={response_text}", "MaxKey SSO UserInfo Response")
        
        # 检查响应状态码
        if user_response.status_code != 200:
            frappe.throw(_("获取用户信息失败: {}").format(response_text))
        
        # 检查响应内容是否为空
        if not user_response.text or user_response.text.strip() == "":
            log_to_file("用户信息响应为空", "MaxKey SSO Error")
            frappe.throw(_("MaxKey 服务器返回了空的用户信息"))
            
        # 直接解析 JSON
        try:
            user_info = user_response.json()
        except json.JSONDecodeError as e:
            log_to_file(f"用户信息 JSON 解析错误: {str(e)}, 原始响应: {response_text}", "MaxKey SSO Error")
            frappe.throw(_("无法解析用户信息响应: {}").format(str(e)))
        
        # 从 MaxKey 返回的用户信息中提取用户 ID
        user_id = user_info.get(config.email_field)
        
        if not user_id:
            log_to_file(f"未找到用户 ID 字段 '{config.email_field}' 在用户信息中: {json.dumps(user_info)}", "MaxKey SSO Error")
            frappe.throw(_("无法从用户信息中获取用户 ID"))
        
        # 处理用户登录或创建
        handle_user(user_id, user_info, config)
        
        # 重定向到首页或原始请求页面
        redirect_to = "/desk"
        
        frappe.local.response["type"] = "redirect"
        frappe.local.response["location"] = redirect_to
        
    except Exception as e:
        log_to_file(f"处理用户信息异常: {str(e)}", "MaxKey SSO Error")
        frappe.throw(_("处理用户信息时出错: {}").format(str(e)))

def handle_user(user_id, user_info, config):
    """
    处理用户登录逻辑
    如果用户不存在，创建新用户
    """
    try:
        # 首先记录更多调试信息
        log_to_file(f"处理用户: user_id={user_id}, auto_create_users={config.auto_create_users}", "MaxKey SSO User")
        log_to_file(f"用户信息: {json.dumps(user_info)}", "MaxKey SSO User")
        
        # 增强用户查找逻辑 - 也通过电子邮件查找
        user_exists = frappe.db.exists("User", user_id)
        user_exists_by_email = frappe.db.exists("User", {"email": user_id})
        
        log_to_file(f"用户检查: exists={user_exists}, exists_by_email={user_exists_by_email}", "MaxKey SSO User")
        
        if user_exists or user_exists_by_email:
            log_to_file(f"用户 {user_id} 已存在，获取用户", "MaxKey SSO User")
            # 如果通过email找到用户，则使用该值
            user_doc_name = user_id
            if not user_exists and user_exists_by_email:
                # 找出通过email匹配的用户名
                user_doc_name = frappe.db.get_value("User", {"email": user_id}, "name")
                log_to_file(f"通过email找到用户: {user_doc_name}", "MaxKey SSO User")
                
            user = frappe.get_doc("User", user_doc_name)
        elif config.auto_create_users:
            # 如果启用了自动创建用户，则创建新用户
            first_name = user_info.get(config.first_name_field, "")
            if not first_name:  # 如果字段为空，尝试使用其他可用字段
                first_name = user_info.get("displayName", "") or user_info.get("realname", "") or user_id
                
            last_name = user_info.get(config.last_name_field, "")
            
            log_to_file(f"正在创建新用户: user_id={user_id}, first_name={first_name}, last_name={last_name}", "MaxKey SSO User Creation")
            
            # 创建用户文档
            user = frappe.new_doc("User")
            user.update({
                "name": user_id,
                "email": user_id,
                "enabled": 1,
                "first_name": first_name,
                "last_name": last_name,
                "user_type": "System User",
                "send_welcome_email": 0
            })
            
            # 添加默认角色
            if hasattr(config, "default_role") and config.default_role:
                log_to_file(f"添加默认角色: {config.default_role}", "MaxKey SSO User Creation")
                user.add_roles(config.default_role)
            
            # 处理角色映射
            if hasattr(config, "role_mappings") and config.role_mappings:
                log_to_file("应用角色映射", "MaxKey SSO User Creation")
                apply_role_mappings(user, user_info, config)
                
            # 保存用户 - 添加重复检查
            user.flags.ignore_permissions = True
            try:
                user.insert(ignore_if_duplicate=True)  # 添加忽略重复项标志
                frappe.db.commit()
                log_to_file(f"新用户创建成功: {user_id}", "MaxKey SSO User Creation")
            except frappe.DuplicateEntryError:
                log_to_file(f"用户 {user_id} 已存在，尝试获取现有用户", "MaxKey SSO User Creation")
                user = frappe.get_doc("User", user_id)
            
        else:
            log_to_file(f"用户 {user_id} 不存在，且未启用自动创建用户", "MaxKey SSO Error")
            frappe.throw(_("用户 {0} 不存在，且系统未配置自动创建用户").format(user_id))
        
        # 检查用户是否启用
        if not user.enabled:
            log_to_file(f"用户 {user_id} 已被禁用", "MaxKey SSO Error")
            frappe.throw(_("用户已被禁用"))
        
        # 登录用户
        log_to_file(f"准备登录用户: {user_id}", "MaxKey SSO Login")
        frappe.local.login_manager.login_as(user.name)  # 使用user.name而不是user_id
        frappe.local.response["home_page"] = "/desk"
        
        log_to_file(f"用户 {user.name} 登录成功", "MaxKey SSO Login")
        
    except Exception as e:
        log_to_file(f"处理用户时出错: {str(e)}", "MaxKey SSO Error")
        # 添加更详细的错误信息
        import traceback
        log_to_file(f"错误详情: {traceback.format_exc()}", "MaxKey SSO Error")
        frappe.throw(_("用户 {0}未找到").format(user_id))

def apply_role_mappings(user, user_info, config):
    """应用角色映射"""
    try:
        # 这里假设 MaxKey 会在用户信息中提供角色数组
        # 实际实现时需要根据 MaxKey 返回的用户信息结构调整
        maxkey_roles = user_info.get("roles", [])
        if not maxkey_roles:
            return
            
        # 获取角色映射
        role_mappings = {}
        for mapping in config.role_mappings:
            role_mappings[mapping.maxkey_role] = mapping.erpnext_role
            
        # 应用角色映射
        for role in maxkey_roles:
            if role in role_mappings:
                user.add_roles(role_mappings[role])
                
    except Exception as e:
        log_to_file(f"应用角色映射时出错: {str(e)}", "MaxKey SSO Error")

def get_config():
    """获取 MaxKey SSO 配置"""
    try:
        return frappe.get_doc("MaxKey SSO Settings")
    except Exception as e:
        log_to_file(f"获取配置时出错: {str(e)}", "MaxKey SSO Error")
        return None

def get_redirect_uri():
    """获取重定向 URI"""
    return get_url("/api/method/erpnext_maxkey_sso.erpnext_maxkey_sso.oauth.callback")

def apply_user_permissions(login_manager):
    """
    用户登录后应用权限
    这个函数会在用户登录后被 on_session_creation 钩子调用
    """
    if hasattr(login_manager, "user"):
        # 在这里添加任何登录后的逻辑
        # 例如更新用户属性、应用特定角色等
        pass

def authorize():
    """OAuth2 authorize endpoint"""
    # 这个只是一个占位符，实际上我们使用 login() 方法
    login()

def get_token():
    """OAuth2 token endpoint - 占位符"""
    frappe.throw(_("直接令牌请求不支持，请使用标准 OAuth 流程"))

def get_userinfo():
    """OAuth2 userinfo endpoint - 占位符"""
    frappe.throw(_("直接用户信息请求不支持，请使用标准 OAuth 流程"))

def get_info():
    """OAuth2 info endpoint - 占位符"""
    frappe.throw(_("直接信息请求不支持，请使用标准 OAuth 流程"))
